// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <iostream>
#include <cstring>
#include "RapidJson.h"
#include "Log.h"

namespace master {

const char* INVALID = "invalid";
const char* WORKER_IP = "ip";
const char* WORKER_PORT = "port";
const char* WORKER_STATUS = "status";
const char* WORKER_FD = "fd";
const char* WORKER_HEARTBEAT_TIME = "heartbeatTime";
const char* WORKER_ASSIGN_TIME = "assignTime";
const char* WORKER_PHONE_ID = "phoneId";

const int WORD_0 = 0;
const int WORD_1 = 1;
const int WORD_2 = 2;
const int WORD_3 = 3;
const int WORD_4 = 4;
const int WORD_5 = 5;
const int WORD_6 = 6;

static JSON_VAL_TYPE_T gJsonValTypeList[] = {
    {REDIS_IDLE,     _eINVAILD, _eINVAILD, _eINVAILD, _eINVAILD, _eINVAILD, _eINVAILD, _eINVAILD},
    {REDIS_WORKER_CAP, _eSTRING,  _eINT,     _eINT,  _eINT, _eUINT64, _eUINT64,_eSTRING},
};

static JSON_STR_NAME_T gJsonStrList[] = {
    {REDIS_IDLE, INVALID, INVALID, INVALID, INVALID,INVALID, INVALID, INVALID},
    {REDIS_WORKER_CAP, WORKER_IP, WORKER_PORT, WORKER_STATUS, WORKER_FD,WORKER_HEARTBEAT_TIME, WORKER_ASSIGN_TIME,WORKER_PHONE_ID},
};

RapidJson::RapidJson()
{
}

RapidJson::~RapidJson()
{
}

void RapidJson::rapidJsonParseWorkerCap(shared_ptr<WorkContextMgT> pWorkerCapInfo, JSON_VAL_T *pJsonVal, int idx)
{
    switch (idx) {
        case WORD_0:
            pWorkerCapInfo->ip = pJsonVal->sVal;
            break;
        case WORD_1:
            pWorkerCapInfo->port = pJsonVal->iVal;
            break;
        case WORD_2:
            pWorkerCapInfo->status = pJsonVal->iVal;
            break;
        case WORD_3:
            pWorkerCapInfo->fd = pJsonVal->iVal;
            break;
        case WORD_4:
            pWorkerCapInfo->heartbeatTime = pJsonVal->fwVal;
            break;
        case WORD_5:
            pWorkerCapInfo->assignTime = pJsonVal->fwVal;
            break;
        case WORD_6:
            pWorkerCapInfo->phoneId = pJsonVal->sVal;
            break;
        default:
            break;
    }
}

void RapidJson::rapidJsonParseGetData(shared_ptr <JSON_INFO_MG_T> pJsonData,
    JSON_VAL_T *pJsonVal, REDIS_KEY_TYPE_E redisKey, int idx)
{
    switch (redisKey) {
        case REDIS_IDLE: {
            LOG_DEBUG_FMT("Redis key is REDIS_IDLE");
        }
        case REDIS_WORKER_CAP: {
            this->rapidJsonParseWorkerCap(pJsonData->data.gWorkerCapInfo, pJsonVal, idx);
        }
        default:
            break;
    }
}

bool RapidJson::rapidJsonParseContent(rapidjson::Document &document,
    std::string key, JSON_VAL_T *pJsonVal, VAL_TYPE_E valType)
{        
    if (!document.HasMember(key.c_str())) {
        LOG_INFO_FMT("Document is no member key %s", key.c_str());
        return true;
    }
    rapidjson::Value &valJson = document[key.c_str()];

    if ((_eINT == valType) && (valJson.IsInt())) {
        pJsonVal->iVal = valJson.GetInt();
    } else if ((_eSTRING == valType) && (valJson.IsString())) {
        errno_t err = strcpy_s(pJsonVal->sVal, MAX_STR_LENGTH, valJson.GetString());
        if (err != ENC_OK) {
            LOG_ERROR_FMT("Strcpy %s failed", pJsonVal->sVal);
            return ENC_FAIL;
        }
    } else if ((_eUINT64 == valType) && (valJson.IsUint64())) {
        pJsonVal->fwVal = valJson.GetUint64();
    } else {
        LOG_ERROR_FMT("Val type is invalid");
        return false;
    }

    return true;
}

void RapidJson::rapidJsonParseString(std::string &str, REDIS_KEY_TYPE_E redisKey,
                                     shared_ptr <WorkContextMgT> &pWorkerCapInfo)
{
    int ret = ENC_OK;
    rapidjson::Document document;
    JSON_VAL_T gJsonVal;
    std::string key;
    VAL_TYPE_E valType;
    int i = 0;
    int idx = 0;

    shared_ptr <JSON_INFO_MG_T> gJsonData = make_shared<JSON_INFO_MG_T>();
    gJsonData->data.gWorkerCapInfo = make_shared<WorkContextMgT>();

    document.Parse(str.c_str());

    if (document.HasParseError()) {
        LOG_ERROR_FMT("Parse error: (%d:%d)%s", document.GetParseError(),
                          document.GetErrorOffset(), rapidjson::GetParseError_En(document.GetParseError()));
        return;
    }

    for (i = 0; i < sizeof(gJsonStrList) / sizeof(gJsonStrList[0]); i++) {
        if (redisKey == gJsonStrList[i].keyType) {
            idx = i;
            break;
        }
    }

    for (i = 0; i < MAX_JSON_NUM; i++) {
        key = gJsonStrList[idx].sKey[i];
        valType = gJsonValTypeList[idx].valType[i];

        if ((_eINVAILD == valType) || (INVALID == key)) {
            break;
        }

        ret = memset_s(&gJsonVal, sizeof(JSON_VAL_T), 0, sizeof(JSON_VAL_T));
        if (ret < 0) {
            LOG_ERROR_FMT("Set gJsonVal struct to 0 memset_s failed");
            break;
        }
        if (true != rapidJsonParseContent(document, key, &gJsonVal, valType)) {
            LOG_ERROR_FMT("Rapid json parse content failed");
        }
        this->rapidJsonParseGetData(gJsonData, &gJsonVal, redisKey, i);
    }

    pWorkerCapInfo = gJsonData->data.gWorkerCapInfo;
}

void RapidJson::rapidJsonCreateStringWorkerCap(shared_ptr<WorkContextMgT> pWorkerCapInfo, std::string &sWorkerCap)
{
    rapidjson::StringBuffer buffer;
    rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);

    writer.StartObject();

    writer.Key(WORKER_IP);
    writer.String(pWorkerCapInfo->ip.c_str());

    writer.Key(WORKER_PORT);
    writer.Int(pWorkerCapInfo->port);

    writer.Key(WORKER_STATUS);
    writer.Int(pWorkerCapInfo->status);

    writer.Key(WORKER_FD);
    writer.Uint64(pWorkerCapInfo->fd);

    writer.Key(WORKER_HEARTBEAT_TIME);
    writer.Uint64(pWorkerCapInfo->heartbeatTime);

    writer.Key(WORKER_ASSIGN_TIME);
    writer.Uint64(pWorkerCapInfo->assignTime);

    writer.Key(WORKER_PHONE_ID);
    writer.String(pWorkerCapInfo->phoneId.c_str());

    writer.EndObject();
    sWorkerCap = buffer.GetString();
}
};
